# Import packages for this project
import os
import numpy as np
import pandas as pd
import geopandas as gpd
import xarray as xr
import rioxarray
import contextily as ctx
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
# Set pandas to display all columns
pd.set_option("display.max_columns", None)
# Load fire perimeter datasets
eaton = gpd.read_file(os.path.join('data',
'fire_perimeters',
'Eaton_Perimeter_20250121.geojson'))
palisades = gpd.read_file(os.path.join('data',
'fire_perimeters',
'Palisades_Perimeter_20250121.geojson'))
# Import NetCDF dataset
landsat = xr.open_dataset(os.path.join('data',
'landsat8-2025-02-23-palisades-eaton.nc'))Github Repository: https://github.com/jwonyk/ep-wildfire-analysis
About
Purpose
In early 2025, two major Southern California wildfires, the Eaton and the Palisades Fire, burned across different parts of Los Angeles County. To better understand the impacts of these events, this project combines Landsat 8 satellite imagery with census-tract-level community indicators from the 2024 Environmental Justice Index (EJI). By integrating remote sensing and social vulnerability data, this analysis highlights both the visible physical burn patterns from space and the community characteristics of affected populations.
Highlights
- Restore missing CRS from a Landsat NetCDF file
- Create true and false-color images to visualize vegetation, burned areas, and the landscape condition
- Overlay false-color imagery with the fire perimeter to illustrate aftermath of wildfire
- Examined key vulnerability indicator: age
- Compared the vulnerability profiles of communities affected by the Palisades and Eaton fires
About the data
Fire Perimeter Dataset
The fire perimeter dataset contains dissolved burn boundaries for the Eaton and Palisades fires. The original data comes from NIFC FIRIS (Fire Integrated Real-time Intelligence System). The data provides daily fire growth during the event of wildfire. The dataset is accessed through Los Angeles GeoHub and it is also accesscible from NIFC FIRIS public ArcGIS service.
Landsat NetCDF Dataset
The Landsat used in this analysis is a Landsat 8 Collection Level-2 Surface Reflectance product accessed through the Microsoft Planetary Computer Data Catalogue. The dataset includes multiple spectral bands stored in a NetCDF structure, CRS information, coordinate dimensions, and metadata. The spectral bands will assist to create true and false-color composite images for post-fire assessment.
Environmental Justice Index Community Vulnerability Analysis
This analysis uses the 2024 Environmental Justice Index (EJI) to identify and compare census tracts affected by the Eaton and Palisades fires, using spatial joins and clipping to the fire boundaries. After aligning datasets to a shared CRS, the intersecting tracts are examined using four key vulnerability indicators, such as age, vehicle access, poverty, and overall EJI score, to highlight differences in social and demographic vulnerability between the two burn areas.
Part 1 - Satellite Imagery Analysis
Setup for the Analysis
Restore Geospatial Information (CRS Recovery)
The NetCDF file contains projection information inside the spatial_ref variable. rio.crs initially shows that no CRS is assigned. I will extract the WKT projection from spatial_ref.crs_wkt and write it back to the dataset using rio.write_crs().
# Extract CRS from the spatial_ref variable
crs_wkt = landsat.spatial_ref.crs_wkt
# Write CRS into rioxarray metadata
landsat = landsat.rio.write_crs(crs_wkt)True-Color Composite
I will select the red, green, and blue bands and converted them into a 3-band array. The initial plot produces warnings due to bright cloud outliers, so I will use the robust = True parameter to ignore extreme values. At the end, I will identify and replace NaN values to create a clean final RGB image.
# Select the red, green, and blue variables
rgb = landsat[['red', 'green', 'blue']].fillna(0)
# Convert it using the `to_array()`
rgb_plot = (rgb.to_array().plot.imshow(
robust = True,
figsize = (7, 7),
add_colorbar = False))
# Add title
plt.title("True Color Composite (Landsat 8, Feb 23 2025)", fontsize = 14)
# Remove all axis
plt.axis("off")
# Improve layout so elements do not overlap
plt.tight_layout()False-Color Composite
False color images use non-visible spectral bands (like near-infrared and shortwave infrared) assigned to visible colors. This enhances differences in vegetation, soil moisture, and burn severity that are not visible in true-color imagery. Following are the colors for the false color images:
Band 4 (Red): Visible red light
Band 5 (NIR): Near-infrared associated with vegetation health
Band 6 (SWIR1): Shortwave infrared associated with burn and moisture response
The composite SWIR1–NIR–Red highlights burned and healthy vegetation contrasts.
# Add `robust = True` to the parameter of `.plot.imshow()`
# This parameter will ignore extreme pixel outliers
(landsat[['swir22', 'nir08', 'red']].fillna(0)
.to_array()
.plot.imshow(
robust = True,
figsize = (7,7),
add_colorbar = False))
# Add title
plt.title("False Color Composite (Landsat 8, Feb 23 2025)", fontsize = 14)
# Remove all axis
plt.axis("off")
# Improve layout so elements do not overlap
plt.tight_layout()Overlaying Fire perimeters
# ---------------------------------------------------------------
# Compute true spatial aspect ratio
# ---------------------------------------------------------------
minx, miny, maxx, maxy = landsat.rio.bounds()
aspect_ratio = (maxx - minx) / (maxy - miny)
fig_height = 9
fig_width = min(fig_height * aspect_ratio, 9)
# ---------------------------------------------------------------
# Create figure and axes for plotting
# ---------------------------------------------------------------
fig, ax = plt.subplots(figsize = (fig_width, fig_height))
# ---------------------------------------------------------------
# BASE LAYER
# ---------------------------------------------------------------
# Plot the false color landsat composite (SWIR2, NIR, Red)
(landsat[['swir22', 'nir08', 'red']].fillna(0)
.to_array()
.plot.imshow(ax = ax,
add_colorbar = False,
robust = True))
# ---------------------------------------------------------------
# FIRE PERIMETER OVERLAYS
# ---------------------------------------------------------------
# Plot Eaton Fire perimeter using the raster's CRS
eaton.to_crs(landsat.rio.crs).plot(ax = ax,
edgecolor = 'yellow',
facecolor = 'none',
linewidth = 1.5)
# Plot Palisades Fire perimeter using the raster's CRS
palisades.to_crs(landsat.rio.crs).plot(ax = ax,
edgecolor = 'red',
facecolor = 'none',
linewidth = 1.5)
# ---------------------------------------------------------------
# ON-MAP TEXT LABELS
# ---------------------------------------------------------------
# Eaton label (top right)
ax.text(0.75, 0.80,
'Eaton Fire',
transform = ax.transAxes,
fontsize = 20,
color = 'yellow',
fontweight = 'bold',
bbox = dict(boxstyle = 'round',
facecolor = 'black',
alpha = 0.5))
# Palisades label (bottom left)
ax.text(0.15, 0.20,
'Palisades Fire',
transform = ax.transAxes,
fontsize = 20,
color = 'red',
fontweight = 'bold',
bbox = dict(boxstyle = 'round',
facecolor = 'white',
alpha = 0.75))
# ---------------------------------------------------------------
# TITLE AND LAYOUT
# ---------------------------------------------------------------
# Label the map to clarify the image and overlays
ax.set_title("False Color Landsat (Feb 23, 2025) " \
"with Fire Perimeters (Jan 21, 2025)",
fontsize = 13)
# Remove both axis ticks
ax.set_xticks([])
ax.set_yticks([])
# Remove x and y label
ax.set_xlabel("")
ax.set_ylabel("")
# Improve layout so elements do not overlap
plt.tight_layout()Figure Description
This map shows a false color Landsat composite with SWIR2, NIR, Red bands over the perimeters for both Eaton and Palisade fires. In this false color image, shortwave infrared (SWIR2) and near-infraed(NIR) energy responses highlight differences between healthy vegetation, burned, and developed areas. Burned area appear in different color compare to healthy vegetation areas are bright green. Overlaying the fire perimeter boundaries on top of the images shows the visual comparison of burn patterns.
Reference
Los Angeles GeoHub / NIFC FIRIS. (2025). Palisades–Eaton dissolved fire perimeters [data file]. Available: https://geohub.lacity.org/maps/ad51845ea5fb4eb483bc2a7c38b2370c/about. [Accessed: Nov. 15, 2025]
U.S. Geological Survey. Landsat Collection 2 Level-2 Surface Reflectance (Microsoft Planetary Computer version) [data file]. Available: https://planetarycomputer.microsoft.com/dataset/landsat-c2-l2. [Accessed: Nov. 15, 2025]
U.S. Department of Health and Human Services. (2024). Environmental Justice Index (EJI), 2024 – California Census Tract Data [data file]. Available: https://www.atsdr.cdc.gov/place-health/php/eji/eji-data-download.html. [Accessed: Nov. 21, 2025]
Wikimedia Commons. (2025). 2025 Southern California Fires and the United States Forest Service (USFS) – Taskforce 1600 at the Palisades Fire (54264940069).jpg [image file]. Available: https://commons.wikimedia.org/wiki/File:2025_Southern_California_fires_and_the_United_States_Forest_Service_(USFS)_-Taskforce_1600_at_the_Palisades_Fire(54264940069).jpg. [Accessed: Dec. 4, 2025]
Citation
@online{kim2025,
author = {Kim, Jay},
title = {Eaton and {Palisades} {Fires:} {A} {Satellite} \& {Social}
{Vulnerability} {Analysis}},
date = {2025-12-03},
url = {https://jwonyk.github.io/posts/ep-wildfire-analysis/},
langid = {en}
}